#include <mysql/mysql.h>
#include "StdMysql.h"
#include <queue>
#include <mutex>
#include <iostream>
struct stdMysqlPrivate
{
    MYSQL mysql;
    bool isConnected;//是否连接
};

stdMysql::stdMysql():p(new stdMysqlPrivate)
{
    mysql_init(&p->mysql);
    p->isConnected = false;
}
stdMysql::~stdMysql()
{
    if(p->isConnected == true)
    {
        mysql_close(&p->mysql);
    }
    delete p;
}
bool stdMysql::Connect(CStrRef host, CStrRef user, CStrRef passwd, CStrRef databaseName, unsigned int port)
{
    if(mysql_real_connect(&p->mysql,host.c_str(),user.c_str(),passwd.c_str(),databaseName.c_str(),port, nullptr, 0) == nullptr)
    {
        std::cout << "数据库连接失败! :"<<mysql_error(&p->mysql);
        return false;
    }
    std::cout << "数据库连接成功\n";
    p->isConnected = true;
    return true;
}

bool stdMysql::IsConnected()
{
    if(p->isConnected == false)
    {
        return false;
    }
    if(mysql_ping(&p->mysql) != 0)
    {
        std::cout << "mysql连接已经断开!\n" << std::endl;
        return false;
    }
    return true;
}

bool stdMysql::Execute(CStrRef sql)
{
    if(mysql_real_query(&p->mysql, sql.c_str(),sql.size()) != 0)
    {
        std::cout << "语句执行失败:" << mysql_error(&p->mysql) << std::endl;
        return false;
    }
    return true;
}
bool stdMysql::ExecuteTransaction(const StrArray &sqlVec)
{
    if(mysql_autocommit(&p->mysql, false) != 0)
    {
        std::cout << "启动手动事务失败!\n";
        return false;
    }

    for(auto &sql : sqlVec)
    {
        if(Execute(sql) == false)
        {
            mysql_rollback(&p->mysql);
            return false;
        }
    }

    mysql_commit(&p->mysql);
    mysql_autocommit(&p->mysql, true);
    return true;
}
std::vector<StrArray> stdMysql::GetTableInfo(CStrRef sql)
{
    if(Execute(sql) == false)
    {
        return {};//空初始化列表
    }

    //获取结果集
    MYSQL_RES* result = mysql_store_result(&p->mysql);

    if(result == nullptr)
    {
        std::cout << "获取结果为空\n";
        return {};
    }

    //获取列表数
    int filedCount = mysql_field_count(&p->mysql);

    std::vector<StrArray> resultArray;//结果


    while(auto sqlRow = mysql_fetch_row(result))
    {
        StrArray arr;
        for(int i = 0; i < filedCount; i++)
        {
            arr.push_back(sqlRow[i]);
        }
        resultArray.push_back(std::move(arr));//移动构造
    }

    //释放结果集
    mysql_free_result(result);
    return resultArray;
};


struct stdMysqlPoolPrivate
{
    void SetParams(int MaxCount, CStrRef host, CStrRef user, CStrRef passwd, CStrRef databaseName, unsigned int port)
    {
        this->host = host;
        this->user = user;
        this->passwd = passwd;
        this->databaseName = databaseName;
        this->port = port;
        this->MaxCount = MaxCount;
    }
    std::string host;
    std::string user;
    std::string passwd;
    std::string databaseName;
    unsigned int port;

    std::queue<MysqlPtr> MysqlQueue;//连接队列
    std::mutex poolMutex;//连接池锁
    int MaxCount;//最大连接数量
};

stdMysqlPool::stdMysqlPool():p(new stdMysqlPoolPrivate)
{

}

stdMysqlPool* stdMysqlPool::GlobalInstance()
{
    static stdMysqlPool pool;//只创建一个对象
    return &pool;
}

bool stdMysqlPool::SetParams(int MaxCount, CStrRef host, CStrRef user, CStrRef passwd, CStrRef databaseName, unsigned int port)
{
    /* 保存参数 */
    p->SetParams(MaxCount, host, user, passwd, databaseName, port);

    for(int i = 0; i < MaxCount; i++)
    {
        MysqlPtr ptr = std::make_shared<stdMysql>();
        if(ptr->Connect(host, user, passwd, databaseName, port) == false)
        {
            return false;
        }
        p->MysqlQueue.push(ptr);
    }
    return true;
}

//mysql连接是Tcp，当网络不稳定会导致连接自动断开
MysqlPtr stdMysqlPool::GetMysql()
{
    //上锁
    std::lock_guard<std::mutex> lcok(p->poolMutex);
    while(p->MysqlQueue.empty() == false)
    {
        //队头指针处于断开状态
        if(p->MysqlQueue.front()->IsConnected() == false)
        {
            //去掉
            p->MysqlQueue.pop();
        }
        else
        {
            auto ptr = p->MysqlQueue.front();
            p->MysqlQueue.pop();
            return ptr;
        }
    }
    
    MysqlPtr ptr = std::make_shared<stdMysql>();
    if(ptr->Connect(p->host, p->user, p->passwd, p->databaseName, p->port) == false)
    {
        std::cout << "数据库连接失败\n";
        return nullptr;
    }
    return ptr;
}

void stdMysqlPool::CloseMysql(MysqlPtr ptr)
{
    //先检查是否还能用
    if(ptr->IsConnected() == false)
    {
        return;
    }
    //上锁放回连接池
    std::lock_guard<std::mutex> lock(p->poolMutex);
    p->MysqlQueue.push(ptr);
}

stdMysqlPool::~stdMysqlPool()
{
    while(p->MysqlQueue.empty() == false)
    {
        p->MysqlQueue.pop();
    }
    delete p;
}
